#include "StdAfx.h"
#include "camera.h"
#include "glmath.h"

#define PITCH_LIMIT Deg2Rad(70)


Camera::Camera()
:	mHeadingRate(.1),
	mPitchRate(.1),
	mHeadingRads(0),
	mPitchRads(0),
	mMaxForwardVelocity(10.0f),
	mForwardVelocity(5.0f),
	mPosition(Vector3(0,0,0)),
	mView(Vector3(0,0,1)),
	mUp(Vector3(0,1,0)),
	mUpdownVelocity(5.f),
	mMode(Walking)			// stays in the same level all the time...
{
}


/*
 *	add rads to current pitch but keep it between limits
 */
void Camera::ChangePitch(double rads)
{
	rads = Clamp(rads,-mPitchRate,mPitchRate);
	mPitchRads += rads;

	mPitchRads = Clamp(mPitchRate,-PITCH_LIMIT,PITCH_LIMIT);	
}



/*
 *	add rads to current heading 
 *	keep it between -2*PI ... 2*PI 
 */
void Camera::ChangeHeading(double rads)
{
	rads = Clamp(rads,-mHeadingRate,mHeadingRate);
	mHeadingRads += rads;

	mHeadingRads = Clamp(mHeadingRads,-TWO_PI,TWO_PI);	
}


//////////////////////////////////////////////////////////////////////////
/*
 *	add vel to current velocity
 *	keep it between limits
 */
float Camera::ChangeVelocity(float vel)
{
	mForwardVelocity += vel;
	mMaxForwardVelocity = Clamp(mForwardVelocity,
		0.f,mMaxForwardVelocity);

	return mForwardVelocity;
}

//////////////////////////////////////////////////////////////////////////
/*
 *	just set current velocity
 *	
 */
void Camera::SetVelocity(float vel)
{
	mForwardVelocity = Clamp(vel,0.f,mMaxForwardVelocity);
}

//////////////////////////////////////////////////////////////////////////
/*
 *	set heading
 *	keep it between -2PI and 2PI
 */
void Camera::SetHeading(double rads)
{
	mHeadingRads = Clamp(rads,-TWO_PI,TWO_PI);	
}

//////////////////////////////////////////////////////////////////////////
/*
 *	set pitch
 *	clamp between -PITCH_LIMIT and PITCH_LIMIT
 */
void Camera::SetPitch(double rads)
{
	mPitchRads = Clamp(rads,-PITCH_LIMIT,PITCH_LIMIT);	
}

//////////////////////////////////////////////////////////////////////////
/*
 *	set position of the camera
 */
void Camera::SetPosition(const Vector3& position)
{
	mPosition = position;
}

//////////////////////////////////////////////////////////////////////////
void Camera::SetPosition(float x,float y,float z)
{
	mPosition.Set(x,y,z);
}

/*
 *	set view vector of the camera
 */
void Camera::SetView(const Vector3& view)
{
	mView = view;
}

void Camera::SetView(float x,float y,float z)
{
	mView.Set(x,y,z);
}


/*
 *	rotate camera's view vector according to the mouse movement
 *	mousex,mousey : mouse coordinates (obviously)
 *	midx, midy : the center of the window
 */
void Camera::RotateByMouse(int mousex,int mousey,int midx,int midy,double dt)
{
	int deltamouse;
	
	deltamouse = midx-mousex;
	SetHeading(mHeadingRate*dt*deltamouse);

	deltamouse = midy-mousey;
	SetPitch(mPitchRate*dt*deltamouse);



	Vector3 viewdir(mView-mPosition);
	viewdir.Normalize();

	Vector3 axes(Cross3(viewdir,mUp).Normalized());

	mHeading.SetFromAxis(mHeadingRads,0,1,0);
	mPitch.SetFromAxis(mPitchRads,axes);

	mPitch *= mHeading;
	mPitch.RotateVector(viewdir);
	viewdir.Normalize();
	mView = mPosition+viewdir;
}


/*
 *	capture mouse and keyboard and move camera accordingly
 */
void Camera::Update(CView* pview,double dt)
{
	CRect rect;
	CWnd *pwnd = pview->GetParentFrame();
	pwnd->GetClientRect(&rect);
	int midx = rect.TopLeft().x + rect.Width()/2;
	int midy = rect.TopLeft().y + rect.Height()/2;

	// check mouse movement
	// get cursor position
	CPoint mousepos;
	GetCursorPos(&mousepos);

	RotateByMouse(mousepos.x,mousepos.y,midx,midy,dt);
	SetCursorPos(midx,midy);


	// check keyboard
	if(GetKeyState(VK_UP) & 0x80)	// up arrow
	{
		Move(Clamp(float(mForwardVelocity*dt),0.0f,mMaxForwardVelocity));		
	}
	if(GetKeyState(VK_DOWN) & 0x80) //down arrow
	{
		Move(Clamp(float(-mForwardVelocity*dt),-mMaxForwardVelocity,0.0f));		
	}
	if(GetKeyState(VK_LEFT) & 0x80)	// left...
	{
		Strafe(Clamp(float(-mForwardVelocity*dt),-mMaxForwardVelocity,0.0f));		
	}
	if(GetKeyState(VK_RIGHT) & 0x80)	//right...
	{
		Strafe(Clamp(float(mForwardVelocity*dt),0.0f,mMaxForwardVelocity));
	}	
	if(GetKeyState(VK_PRIOR) & 0x80)	// page-up key
	{
		Elevate(float(mUpdownVelocity*dt));
	}
	if (GetKeyState(VK_NEXT) & 0x80)	// page-down key
	{
		Elevate(float(-mUpdownVelocity*dt));
	}
}


/*
 *	ascent (or descent) by adding the same value to both  y of 
 *	position and view vectors
 */
void Camera::Elevate(float vel)
{
	mView.y += vel;
	mPosition.y += vel;
}


/*
 *	
 */
void Camera::Move(float vel)
{
	Vector3 viewdir(mView-mPosition);
	viewdir.Normalize();	
	viewdir *= vel;
	
	//
	// if camera walks (instead of flying), then don't affect the height of camera
	if(mMode==Walking)
		viewdir.y=0;

	mPosition += viewdir;
	mView += viewdir;
}


/*
 *	
 */
void Camera::Strafe(float vel)
{
	// calculate strafe
	Vector3 viewdir(mView-mPosition);
	viewdir.Normalize();
	Vector3 cross(Cross3(viewdir,mUp).Normalized());
	cross *= vel;

	mPosition += cross;
	mView += cross;
}


/*
 *	call gluLookAt with camera's position, view and up vectors	
 */
void Camera::Apply()
{
	gluLookAt(mPosition.x,mPosition.y,mPosition.z,
		mView.x,mView.y,mView.z,
		mUp.x,mUp.y,mUp.z);
}




//
//
//cCamera::cCamera(void)
//{
//}
//
//void cCamera::Init( 
//				   CView *view,
//				   float newSpeed, 
//				   Vector3 newPosition/* =Vector3(0.0,0.0,0.0)*/,
//				   float angleHeading/*=0.0f*/,float anglePitch/*=0.0f*/)
//{
//	m_pview = view;
//
//	mPosition = newPosition;
//	m_speed = newSpeed;
//	m_angleHeading = angleHeading;
//	m_anglePitch = anglePitch;
//
//	/*
//	 	put cursor in the center of the screen
//		edit: no use for it, we hide the cursor anyway
//	 */
//	CRect rect;
//	CWnd *pwnd = m_pview->GetParentFrame();
//	pwnd->GetClientRect(&rect);
//	int x = rect.TopLeft().x + rect.Width()/2;
//	int y = rect.TopLeft().y + rect.Height()/2;
//
//#ifdef TRACE_XY
//	TRACE("cCamera::init:  x=%d and y=%d\n",x,y);
//#endif
//	SetCursorPos(x,y);	
//}
//
//
///************************************************************************
//	calculate the distance to move, based on time passed
//	time is in seconds
//************************************************************************/
//void cCamera::Update(double time
//					 //,const cController *controller
//					 )
//{
//	CRect rect;
//	CWnd *pwnd = m_pview->GetParentFrame();
//	pwnd->GetClientRect(&rect);
//
//	/*static double prevTime=time;
//	double dt = time - prevTime;
//	prevTime = time;*/
//
//	float distance = m_speed*float(time);
//
//	//Get the mouse position
//	POINT mPos;
//	GetCursorPos(&mPos);
//
//	int halfscrwidth = rect.Width()>>1;
//	int halfscrheight = rect.Height()>>1;
//
//	m_angleHeading	+= (float(mPos.x-halfscrwidth-rect.TopLeft().x))*distance*6;
//	m_anglePitch	+= (float(mPos.y-halfscrheight-rect.TopLeft().y))*distance*6;
//
////#define TRACEANGLES
//#ifdef TRACEANGLES
//	TRACE("_angleYaw=%f and _anglePitch=%f\n",_angleYaw,_anglePitch);
//#endif
//	
//
//
//	//make sure angleY is not too great
//	if(m_anglePitch>80.0f)
//		m_anglePitch=80.0f;
//
//	if(m_anglePitch<-80.0f)
//		m_anglePitch=-80.0f;
//
//	/*
//	 	set the mouse back to the center of the screen
//	 */
//	
//	int x = rect.TopLeft().x + rect.Width()/2;
//	int y = rect.TopLeft().y + rect.Height()/2;
//	SetCursorPos(x,y);	
//
//	/*
//		move forward/back or strafe
//	 */
//	if(GetAsyncKeyState(VK_UP) || GetAsyncKeyState(57)) // up arrow or 'W' key
//	{
//		mPosition += Vector3(	(float)sin(Deg2Rad(m_angleHeading))*distance,
//								0,	
//								(float)-cos(Deg2Rad(m_angleHeading))*distance);
//	}
//
//	if(GetAsyncKeyState(VK_DOWN) || GetAsyncKeyState(53)) // down arrow or 'S' key
//	{
//		mPosition -= Vector3(	(float)sin(Deg2Rad(m_angleHeading))*distance,
//								0,	
//								(float)-cos(Deg2Rad(m_angleHeading))*distance);
//	}
//
//	if(GetAsyncKeyState(VK_RIGHT) || GetAsyncKeyState(44)) // right arrow or 'D' key
//	{
//		mPosition += Vector3(	(float)cos(Deg2Rad(m_angleHeading))*distance,
//								0,	
//								(float)sin(Deg2Rad(m_angleHeading))*distance);
//	}
//
//	if(GetAsyncKeyState(VK_LEFT) || GetAsyncKeyState(41)) // left arrow or 'A' key
//	{
//		mPosition -= Vector3(	(float)cos(Deg2Rad(m_angleHeading))*distance,
//								0,	
//								(float)sin(Deg2Rad(m_angleHeading))*distance);
//	}	
//}
//
//
///*
// *	
// */
//void cCamera::Apply()
//{
//	glRotatef(m_anglePitch, 1.0f, 0.0f, 0.0f);
//	glRotatef(m_angleHeading, 0.0f, 1.0f, 0.0f);
//	glTranslatef(-mPosition[0], -mPosition[1], -mPosition[2]);
//}
//
//
//
//
//
//
//
//
///************************************************************************/
///* quat camera 
//
//*/
///************************************************************************/
//
//
//cQuatCamera::cQuatCamera(Vector3 &position/* =Vector3(0,0,0)*/,
//						 double	angleHeading/*=0.0f*/, double anglePitch/*=0.0f*/)
//:	m_pView(NULL),
//	mPitchRate(1.0f),
//	mHeadingRate(1.0f),
//	//m_angleHeading(0),
//	//m_anglePitch(0),
//	// m_maxSpeed(0),
//	m_speed(5.0f)
//	//mPosition(Vector3(0,0,0)),
//	//mUp(Vector3(0,1,0)),
//	//mView(Vector3(0,0,1))
//{
//	Init(position,angleHeading,anglePitch);
//}
//
//
//
///*
// *	initialize camera...
// */
//void cQuatCamera::Init(Vector3 &position,
//					   double	angleHeading, double anglePitch)
//{
//	SetPosition(position);
//	SetRotation(anglePitch,angleHeading,0);
//}
//
//
///*
// *	
// */
//void cQuatCamera::Apply()
//{
//	/*Matrix mat = m_rotation.CreateMatrix();
//	mat.Inverse();*/
//	glMultMatrix(m_rotation.CreateMatrix());
//	glTranslate(-mPosition);	
//}
//
///*
// *	
// */
//void cQuatCamera::Update(const CView* pview,double dt)
//{
//	double pitch;
//	double heading;
//	int mousedelta;
//	int midx,midy;
//	
//	// get active view's dimension
//	CRect rect;
//	CWnd *pwnd = pview->GetParentFrame();
//	pwnd->GetClientRect(&rect);
//
//	// find the center coords
//	midx = rect.Width()>>1;
//	midy = rect.Height()>>1;
//
//	// check mouse movement
//	// get cursor position
//	CPoint mousepos;
//	GetCursorPos(&mousepos);
//	
//	
//
//	// rotate the camera
//	mousedelta = mousepos.x-midx-rect.TopLeft().x;
//	heading = mHeadingRate*dt*mousedelta;
//	//if(heading>95)	heading
//
//	mousedelta = mousepos.y-midy-rect.TopLeft().y;
//	pitch = mPitchRate*dt*mousedelta;
//	
//	// ensure that pitch is not going too high
//	if(pitch>1.4835298642)	// ~85 degrees
//		pitch=1.4835298642;
//	else if(pitch<-1.4835298642)
//		pitch=-1.4835298642;
//
//	//RotateBy(pitch,heading,0);	// change the rotation of the camera
//	RotateBy(heading,0,1,0);	
//	RotateBy(pitch,1,0,0);
//	
//	
//	// vector v points towards the z axis
//	// represents how much the camera must move IF a key is pressed
//	Vector3 v(0,0,1);	
//	// make vector v point to where the camera looks
//	m_rotation.RotateVector(v);
//
//
//	// check keyboard commands and move accordingly
//	if(GetKeyState(VK_UP) & 0x80)
//		MoveBy(-v*float(m_speed*dt));	// move forward by v
//	if(GetKeyState(VK_DOWN) & 0x80)
//		MoveBy(v*float(m_speed*dt));	// move backward by v
//	if(GetKeyState(VK_LEFT) & 0x80)
//	{
//		Vector3 vv(-v.z,0,v.x);
//		vv.Normalize();
//		MoveBy(vv*float(m_speed*dt));
//	}
//	if(GetKeyState(VK_RIGHT) & 0x80)
//	{
//		Vector3 vv(v.z,0,-v.x);
//		vv.Normalize();
//		MoveBy(vv*float(m_speed*dt));
//	}	
//	
//	
//
//	// finally, set mouse cursor back to the center of the view
//	SetCursorPos(midx+rect.TopLeft().x,midy+rect.TopLeft().y);
//}
//
//
//
//
//#ifdef _DEBUG
//void cQuatCamera::Test()
//{
//	Vector3 pos(0,0,1);
//    Quaternion rot;
//
//	double pitch = PIdiv4;
//	double heading = PIdiv4;
//
//	rot.SetFromRadians(pitch,heading,0);
//	rot.RotateVector(pos,false);
//	
//	rot.Identity();
//
//	
//	rot *= Quaternion().SetFromAxis(heading,0,1,0);
//	rot *= Quaternion().SetFromAxis(pitch,1,0,0);
//
//	pos.Set(0,0,1);
//	rot.RotateVector(pos,false);
//
//	rot.Normalize();
//
//	TRACE("blah");
//
//	SetPosition(Vector3(0,0,20));
//	m_rotation.Identity();
//
//	pos.Set(0,0,1);
//	RotateBy(heading,0,1,0);
//	m_rotation.RotateVector(pos);
//	//pos.Set(0,0,1);
//	//RotateBy(pitch,1,0,0);
//	//m_rotation.RotateVector(pos);
//	pos.Set(0,0,1);
//	RotateBy(heading,0,1,0);
//	m_rotation.RotateVector(pos);
//
//
//	pos.Set(0,0,1);
//	m_rotation.Identity();
//	RotateBy(pitch,heading,0);
//	m_rotation.RotateVector(pos);
//
//	TRACE("another blah");
//}
//#endif
//
//
//
//
//
///************************************************************************/
///* 
//	flying camera
//*/
///************************************************************************/
//
//cFlyingCamera::cFlyingCamera()
//: m_currentRotationAngle(0)
//{
//}
//
//
//
//
//void cFlyingCamera::Init( Vector3 &position,  Vector3 &view, Vector3 &up)
//{
//	m_pos = position;
//	mView = view;
//	mUp = up;
//}
//
//
//
//void cFlyingCamera::Move(float direction)
//{
//	// First we need to get the direction at which we are looking.
//	Vector3 lookDir;
//
//	// The look direction is the view (where we are looking) 
//	// minus the position (where we are).
//	lookDir = mView-m_pos;
//
//	// Normalize the direction.
//	lookDir.Normalize();
//
//	// Call UpdateCamera to move our camera in the direction we want.
//	Update(lookDir,direction);
//
//}
//
//
//
//
//void cFlyingCamera::Update()
//{
//}